"""
Bootstrap snippet to embed into standalone scripts.
Contains minimal implementations of start/end/progress and plotting helpers.
"""
from pathlib import Path
import os
import sys
from typing import Optional, Dict, Any

from rich.console import Console
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeElapsedColumn, MofNCompleteColumn
from rich.panel import Panel
from rich.align import Align
from rich.table import Table
from rich import box

console = Console()
 

def set_chinese_font(font_family: str = "Heiti TC") -> None:
    try:
        import matplotlib.pyplot as plt
        plt.rcParams['font.sans-serif'] = [font_family]
        plt.rcParams['axes.unicode_minus'] = False
        console.log(f"字体配置已加载：{plt.rcParams.get('font.sans-serif')}")
    except Exception as e:
        console.log(f"设置中文字体时出错（内联）：{e}")


def detect_chinese_font(preferred: list[str] | None = None) -> str | None:
    """Return the name of the first available Chinese font from the preferred list, or None."""
    try:
        import matplotlib.font_manager as fm
        if preferred is None:
            preferred = [
                'Heiti TC', 'PingFang SC', 'STHeiti', 'Songti SC', 'Hiragino Sans GB',
                'Microsoft YaHei', 'SimHei', 'Noto Sans CJK SC', 'PingFang', 'Apple LiGothic'
            ]
        # Build a mapping of family names found on the system by inspecting font files
        family_names = set()
        for font_path in fm.findSystemFonts(fontpaths=None, fontext='ttf') + fm.findSystemFonts(fontpaths=None, fontext='otf'):
            try:
                fp = fm.FontProperties(fname=font_path)
                name = fp.get_name()
                if name:
                    family_names.add(name)
            except Exception:
                continue
        # Try preferred list first
        for name in preferred:
            if name in family_names:
                return name
        # Fallback: pick any family name that looks like a CJK font
        for candidate in family_names:
            low = candidate.lower()
            if any(key in low for key in ('hei', 'song', 'fang', 'noto', 'yahei', 'simhei', 'li')):
                return candidate
    except Exception as e:
        console.log(f"detect_chinese_font 出错：{e}")
    return None


def set_plot_style(style_cfg: Dict[str, Any] = None) -> None:
    import matplotlib
    import matplotlib.pyplot as plt
    if style_cfg is None:
        style_cfg = {'style': 'darkgrid', 'dpi': 300, 'figsize': [8,6], 'bbox_inches': 'tight', 'font_family':'Heiti TC'}
    # choose a usable Chinese font if available
    preferred = [style_cfg.get('font_family', 'Heiti TC')]
    detected = detect_chinese_font(preferred)
    font_family = detected or style_cfg.get('font_family', 'Heiti TC')
    # Prefer the detected/desired font as the default family to improve CJK rendering
    matplotlib.rcParams['font.family'] = font_family
    # Also keep it in the sans-serif fallback list for libraries that query that
    matplotlib.rcParams['font.sans-serif'] = [font_family]
    matplotlib.rcParams['axes.unicode_minus'] = False
    try:
        import seaborn as sns
        sns.set_style(style_cfg.get('style', 'darkgrid'))
    except Exception:
        matplotlib.style.use(style_cfg.get('style', 'default'))
    plt.rcParams['figure.dpi'] = style_cfg.get('dpi', 300)
    plt.rcParams['figure.figsize'] = style_cfg.get('figsize', [8,6])
    plt.rcParams['savefig.bbox'] = style_cfg.get('bbox_inches', 'tight')


def force_chinese_font(ax, font_family: str = 'Heiti TC'):
    """
    Force-apply a Chinese font to the axis texts. `font_family` may be a family name
    or a path to a font file. This sets FontProperties(fname=...) on text objects so
    matplotlib will use the specified font file when rendering/saving.
    """
    try:
        import matplotlib.font_manager as fm
        import matplotlib as mpl
        from matplotlib.font_manager import FontProperties

        # Determine font file path
        font_path = None
        # If looks like a file path, use it
        if isinstance(font_family, str) and (font_family.endswith('.ttf') or font_family.endswith('.otf') or '/' in font_family or '\\' in font_family):
            font_path = font_family
        else:
            try:
                prop = FontProperties(family=font_family)
                font_path = fm.findfont(prop)
            except Exception:
                font_path = None

        fp = FontProperties(fname=font_path) if font_path else None

        # apply to tick labels, axis labels, title, legend
        texts = []
        texts.extend(ax.get_xticklabels())
        texts.extend(ax.get_yticklabels())
        try:
            texts.append(ax.xaxis.label)
            texts.append(ax.yaxis.label)
            texts.append(ax.title)
        except Exception:
            pass

        for t in texts:
            try:
                if fp:
                    t.set_fontproperties(fp)
                else:
                    t.set_fontfamily(font_family)
            except Exception:
                pass

        legend = ax.get_legend()
        if legend:
            try:
                if fp:
                    # legend title
                    try:
                        legend.get_title().set_fontproperties(fp)
                    except Exception:
                        pass
                    for text in legend.get_texts():
                        try:
                            text.set_fontproperties(fp)
                        except Exception:
                            pass
                else:
                    for text in legend.get_texts():
                        try:
                            text.set_fontfamily(font_family)
                        except Exception:
                            pass
            except Exception:
                pass
    except Exception as e:
        console.log(f"force_chinese_font 内联出错：{e}")


def font_path_for_family(family_name: str) -> str | None:
    """Return a font file path that matplotlib would use for the given family name."""
    try:
        import matplotlib.font_manager as fm
        from matplotlib.font_manager import FontProperties
        prop = FontProperties(family=family_name)
        return fm.findfont(prop)
    except Exception:
        return None


def start(label: Optional[str] = None) -> float:
    import time
    label = f" {label}" if label else ""
    start_time_str = time.strftime('%Y-%m-%d %H:%M:%S')
    start_time = time.time()
    try:
        # include an emoji for visual cue
        panel = Panel(Align.center(f"🚀 开始{label}\n{start_time_str}", vertical="middle"), title="开始", border_style="green")
        console.print(panel)
    except Exception:
        console.print(start_time_str)
    return start_time


def end(start_time: float, label: Optional[str] = None) -> None:
    import time
    label = f" {label}" if label else ""
    end_time_str = time.strftime('%Y-%m-%d %H:%M:%S')
    duration = time.time() - start_time
    try:
        # format duration as H:M:S
        secs = int(duration)
        hours, rem = divmod(secs, 3600)
        mins, secs = divmod(rem, 60)
        hhmmss = f"{hours:02d}:{mins:02d}:{secs:02d}"
        human = []
        if hours:
            human.append(f"{hours}小时")
        if mins:
            human.append(f"{mins}分")
        human.append(f"{secs}秒")
        human_str = ''.join(human)
        panel = Panel(Align.center(f"✅ 结束{label}\n{end_time_str}\n耗时 {human_str}（{hhmmss}）", vertical="middle"), title="结束", border_style="bright_green")
        console.print(panel)
    except Exception:
        console.print(f"结束{label} {end_time_str}，耗时 {int(duration)} 秒")


def print_summary_table(rows: list[tuple], columns: list[str] | None = None, title: str = "运行摘要") -> None:
    """Print a simple three-line-like table (header, body, footer) using Rich.

    rows: list of tuples representing table rows
    columns: list of column names
    """
    try:
        table = Table(title=title, box=box.SIMPLE_HEAVY)
        if not columns:
            # infer number of columns
            if rows and len(rows) > 0:
                columns = [f"列{i+1}" for i in range(len(rows[0]))]
            else:
                columns = []
        for col in columns:
            table.add_column(col, style="cyan", no_wrap=True)
        def fmt(x):
            # format floats to 3 decimal places, leave others as str
            try:
                if isinstance(x, float):
                    return f"{x:.3f}"
                # numpy float types
                import numbers
                if isinstance(x, numbers.Real) and not isinstance(x, bool):
                    # convert to float then format
                    return f"{float(x):.3f}"
            except Exception:
                pass
            return str(x)

        for r in rows:
            table.add_row(*[fmt(x) for x in r])
        console.print(table)
    except Exception as e:
        console.log(f"打印摘要表出错：{e}")


def log_saved(path: str | Path | os.PathLike, label: Optional[str] = None) -> None:
    """Log a saved file with timestamp and size in a small Rich panel.

    path: file path (str or PathLike)
    label: optional human label (e.g., task name)
    """
    try:
        from datetime import datetime
        p = Path(path)
        ts = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        size = None
        if p.exists() and p.is_file():
            try:
                size_bytes = p.stat().st_size
                if size_bytes < 1024:
                    size = f"{size_bytes} B"
                elif size_bytes < 1024**2:
                    size = f"{size_bytes/1024:.1f} KB"
                else:
                    size = f"{size_bytes/1024**2:.2f} MB"
            except Exception:
                size = None

        label = f" {label}" if label else ""
        # choose emoji by extension
        suffix = p.suffix.lower()
        if suffix in ('.png', '.jpg', '.jpeg', '.gif'):
            emoji = '🖼️'
        else:
            emoji = '💾'

        body = f"{emoji} 已保存{label}\n路径: {p}\n时间: {ts}"
        if size:
            body += f"\n大小: {size}"

        panel = Panel(Align.left(body), title="已保存", border_style="blue")
        console.print(panel)
    except Exception as e:
        console.log(f"log_saved 出错：{e}")


def progress(*, total: int = 100, **kwargs) -> Progress:
    spinner_style = kwargs.pop('spinner_style', 'green')
    complete_style = kwargs.pop('complete_style', 'bright_green')
    description_template = kwargs.pop('description_template', '[bold]{task.description}')
    show_spinner = kwargs.pop('show_spinner', True)
    columns = []
    if show_spinner:
        columns.append(SpinnerColumn(style=spinner_style))
    columns.append(TextColumn(description_template))
    columns.append(BarColumn(bar_width=None, complete_style=complete_style))
    columns.append(MofNCompleteColumn())
    columns.append(TimeElapsedColumn())
    return Progress(*columns, **kwargs)


# --- joblib compatibility shim ---
try:
    from joblib import Parallel, delayed
    def _joblib_available():
        return True
except Exception:
    # provide a very small fallback that implements the same call signature
    def _joblib_available():
        return False
    def delayed(func, *dargs, **dkwargs):
        # return a callable that when called executes func
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        return wrapper
    class Parallel:
        def __init__(self, n_jobs=1, **kwargs):
            self.n_jobs = n_jobs
        def __call__(self, tasks):
            # tasks is an iterable of callables (results producers)
            res = []
            for t in tasks:
                try:
                    res.append(t())
                except Exception as e:
                    res.append(e)
            return res



# Auto-apply plot style when this snippet is executed as a script (embedded standalone)
# or when the environment variable GMT_TEMPLATE_AUTO_STYLE is set to '1'.
try:
    _should_auto = ( __name__ == '__main__' ) or (str(__import__('os').environ.get('GMT_TEMPLATE_AUTO_STYLE','0'))=='1')
except Exception:
    _should_auto = False

if _should_auto:
    try:
        set_plot_style()
    except Exception:
        pass
